package orm

import (
	t "gitee.com/KotlinToGo/type"
	"strings"
)

// Orm 对象关系映射模块, 所有的orm操作, 都在这里完成。
type Orm struct {
	ISession
	*Api
	driver     string
	bindValues []interface{}
}

var _ IOrm = (*Orm)(nil)

// NewDB ...
func NewOrm(e IEngine) *Orm {
	var orm = new(Orm)
	orm.SetISession(NewSession(e))
	orm.Api = new(Api)
	return orm
}


// Close ...
func (p *Orm) Close() {
	p.GetISession().Close()
}

// ExtraCols 额外的字段
func (p *Orm) ExtraCols(args ...string) IOrm {
	p.extraCols = append(p.extraCols, args...)
	return p
}

// ResetExtraCols ...
func (p *Orm) ResetExtraCols() IOrm {
	p.extraCols = []string{}
	return p
}

// SetBindValues ...
func (p *Orm) SetBindValues(v interface{}) {
	p.bindValues = append(p.bindValues, v)
}

// ClearBindValues ...
func (p *Orm) ClearBindValues() {
	p.bindValues = []interface{}{}
}

// GetBindValues ...
func (p *Orm) GetBindValues() []interface{} {
	return p.bindValues
}

// GetDriver ...
func (p *Orm) GetDriver() string {
	return p.driver
}

// SetISession ...
func (p *Orm) SetISession(is ISession) {
	p.ISession = is
}

// GetISession ...
func (p *Orm) GetISession() ISession {
	return p.ISession
}

// GetOrmApi ...
func (p *Orm) GetOrmApi() *Api {
	return p.Api
}

// Fields : select fields
func (p *Orm) Table(tableName interface{}) IOrm {
	p.GetISession().Bind(tableName)
	//dba.table = dba.GetISession().GetTableName()
	return p
}

// Fields : select fields 替换为最新字段
func (p *Orm) Field(fields ...string) IOrm {
	p.fields = fields
	return p
}

// AddFields :如果您已经具有查询生成器实例，并且希望将列添加到其现有的select子句中，则可以使用AddFields方法。
func (p *Orm) AddField(fields ...string) IOrm {
	p.fields = append(p.fields, fields...)
	return p
}

// Distinct : select distinct
func (p *Orm) Distinct() IOrm {
	p.distinct = true
	return p
}

// Data : insert or update data
func (p *Orm) Data(data interface{}) IOrm {
	p.data = data
	return p
}

// Group : select group by
func (p *Orm) Group(group string) IOrm {
	p.group = group
	return p
}

// Having : select having
func (p *Orm) Having(having string) IOrm {
	p.having = having
	return p
}

// Order : select order by
func (p *Orm) Order(order string) IOrm {
	p.order = order
	return p
}

// Limit : select limit
func (p *Orm) Limit(limit int) IOrm {
	p.limit = limit
	return p
}

// Offset : select offset
func (p *Orm) Offset(offset int) IOrm {
	p.offset = offset
	return p
}

// Page : select page
func (p *Orm) Page(page int) IOrm {
	p.offset = (page - 1) * p.GetLimit()
	return p
}

// Where : 查询或执行条件，关系是 and。
//	args[0]是被操作的字段，args[1] 是where的条件，比如: '<'，'='，'>'，args[2]是具体的值。
func (p *Orm) Where(args ...interface{}) IOrm {
	if len(args) == 0 ||
		t.New(args[0]).Bool() == false {
		return p
	}
	// 如果只传入一个参数, 则可能是字符串、一维对象、二维数组
	// 重新组合为长度为3的数组, 第一项为关系(and/or), 第二项为具体传入的参数 []interface{}
	w := []interface{}{"and", args}
	p.where = append(p.where, w)
	return p
}

func (p *Orm) And(args ...interface{}) IOrm {
	// 如果只传入一个参数, 则可能是字符串、一维对象、二维数组
	// 重新组合为长度为3的数组, 第一项为关系(and/or), 第二项为具体传入的参数 []interface{}
	w := []interface{}{"and", args}
	p.where = append(p.where, w)
	return p
}

// Or : query or execute where condition, the relation is and
func (p *Orm) Or(args ...interface{}) IOrm {
	// 如果只传入一个参数, 则可能是字符串、一维对象、二维数组
	// 重新组合为长度为3的数组, 第一项为关系(and/or), 第二项为具体传入的参数 []interface{}
	w := []interface{}{"or", args}
	p.where = append(p.where, w)
	return p
}

// Null ...
func (p *Orm) Null(arg string) IOrm {
	return p.Where(arg + " IS NULL")
}

// OrNull ...
func (p *Orm) OrNull(arg string) IOrm {
	return p.Or(arg + " IS NULL")
}

// NotNull ...
func (p *Orm) NotNull(arg string) IOrm {
	return p.Where(arg + " IS NOT NULL")
}

// OrNotNull ...
func (p *Orm) OrNotNull(arg string) IOrm {
	return p.Or(arg + " IS NOT NULL")
}

// Regexp ...
func (p *Orm) Regexp(arg string, expStr string) IOrm {
	return p.Where(arg, "REGEXP", expStr)
}

// OrRegexp ...
func (p *Orm) OrRegexp(arg string, expStr string) IOrm {
	return p.Or(arg, "REGEXP", expStr)
}

// NotRegexp ...
func (p *Orm) NotRegexp(arg string, expStr string) IOrm {
	return p.Where(arg, "NOT REGEXP", expStr)
}

// OrNotRegexp ...
func (p *Orm) OrNotRegexp(arg string, expStr string) IOrm {
	return p.Or(arg, "NOT REGEXP", expStr)
}

// In ...
func (p *Orm) In(needle string, hyStack []interface{}) IOrm {
	return p.Where(needle, "IN", hyStack)
}

// OrIn ...
func (p *Orm) OrIn(needle string, hyStack []interface{}) IOrm {
	return p.Or(needle, "IN", hyStack)
}

// NotIn ...
func (p *Orm) NotIn(needle string, hyStack []interface{}) IOrm {
	return p.Where(needle, "NOT IN", hyStack)
}

// OrNotIn ...
func (p *Orm) OrNotIn(needle string, hyStack []interface{}) IOrm {
	return p.Or(needle, "NOT IN", hyStack)
}

// Between ...
func (p *Orm) Between(needle string, hyStack []interface{}) IOrm {
	return p.Where(needle, "BETWEEN", hyStack)
}

// OrBetween ...
func (p *Orm) OrBetween(needle string, hyStack []interface{}) IOrm {
	return p.Or(needle, "BETWEEN", hyStack)
}

// NotBetween ...
func (p *Orm) NotBetween(needle string, hyStack []interface{}) IOrm {
	return p.Where(needle, "NOT BETWEEN", hyStack)
}

// OrNotBetween ...
func (p *Orm) OrNotBetween(needle string, hyStack []interface{}) IOrm {
	return p.Or(needle, "NOT BETWEEN", hyStack)
}

// Join : select join query
func (p *Orm) Join(args ...interface{}) IOrm {
	p.joinBuilder("INNER", args)
	return p
}

// LeftJoin ...
func (p *Orm) LeftJoin(args ...interface{}) IOrm {
	p.joinBuilder("LEFT", args)
	return p
}

// RightJoin ...
func (p *Orm) RightJoin(args ...interface{}) IOrm {
	p.joinBuilder("RIGHT", args)
	return p
}

// CrossJoin ...
func (p *Orm) CrossJoin(args ...interface{}) IOrm {
	p.joinBuilder("CROSS", args)
	return p
}

// joinBuilder
func (p *Orm) joinBuilder(joinType string, args []interface{}) {
	p.join = append(p.join, []interface{}{joinType, args})
}

// Reset  orm api and bind values reset to init
func (p *Orm) Reset() IOrm {
	p.Api = new(Api)
	p.ClearBindValues()
	p.ResetUnion()
	p.ResetTable()
	p.ResetWhere()
	p.ResetExtraCols()
	return p
}

// ResetTable ...
func (p *Orm) ResetTable() IOrm {
	p.GetISession().SetIBinder(NewBinder())
	return p
}

// ResetWhere ...
func (p *Orm) ResetWhere() IOrm {
	p.where = [][]interface{}{}
	return p
}

// ResetUnion ...
func (p *Orm) ResetUnion() IOrm {
	p.GetISession().SetUnion(nil)
	return p
}

// Sql (原BuildSql)，构建 Sql 指令。即: CRUD
//	参数<operationType>只能是(select, insert, update, delete)
func (p *Orm) Sql(operationType ...string) (a string, b []interface{}, err error) {
	// 解析table
	p.table, err = p.GetISession().GetTableName()
	if err != nil {
		p.GetISession().GetIEngine().GetLogger().Error(err.Error())
		return
	}
	// 解析字段
	// 如果有union操作, 则不需要
	if inArray(p.GetIBinder().GetBindType(), []interface{}{ObjectStruct, ObjectStructSlice}) &&
		p.GetUnion() == nil {
		p.fields = getTagName(p.GetIBinder().GetBindResult(), TagName)
	}
	if len(operationType) == 0 || (len(operationType) > 0 && strings.ToLower(operationType[0]) == "select") {
		//// 根据传入的struct, 设置limit, 有效的节约空间
		//if dba.union == "" {
		//	var bindType = dba.GetIBinder().GetBindType()
		//	if bindType == OBJECT_MAP || bindType == OBJECT_STRUCT {
		//		dba.Limit(1)
		//	}
		//}
		a, b, err = NewBuilder(p.GetISession().GetIEngine().GetDriver()).BuildQuery(p)
		if p.GetISession().GetTransaction() {
			a = a + p.GetPessimisticLock()
		}
	} else {
		a, b, err = NewBuilder(p.GetISession().GetIEngine().GetDriver()).BuildExecute(p, strings.ToLower(operationType[0]))
		// 重置强制获取更新或插入的字段, 防止复用时感染
		p.ResetExtraCols()
	}
	// 如果是事务, 因为需要复用单一对象, 故参数会产生感染
	// 所以, 在这里做一下数据绑定重置操作
	if p.GetISession().GetTransaction() {
		p.Reset()
	}
	// 这里统一清理一下绑定的数据吧, 万一要复用了, 造成绑定数据感染, 就尴尬了
	p.ClearBindValues()
	return
}

// Transaction ...
func (p *Orm) TX(closers ...func(db IOrm) error) (err error) {
	err = p.ISession.Begin()
	if err != nil {
		p.GetIEngine().GetLogger().Error(err.Error())
		return err
	}

	for _, closer := range closers {
		err = closer(p)
		if err != nil {
			p.GetIEngine().GetLogger().Error(err.Error())
			_ = p.ISession.Rollback()
			return
		}
	}
	return p.ISession.Commit()
}

// SharedLock 共享锁
// select * from xxx lock in share mode
func (p *Orm) SharedLock() *Orm {
	p.pessimisticLock = " lock in share mode"
	return p
}

// LockForUpdate
// select * from xxx for update
func (p *Orm) LockForUpdate() *Orm {
	p.pessimisticLock = " for update"
	return p
}
